# -*- coding: utf-8 -*-
""" Генератор ipfilter.dat из файла со списком подсетей.
    1 строка - 1 подсеть.
    Форматы:
    IP Mask - xxx.xxx.xxx.xxx yyy.yyy.yyy.yyy
    IP/CIDR - xxx.xxx.xxx.xxx/yy
    Файл с диапазоном адресов: IPRangesRaw.txt
"""

import re
import operator

# Определяем функции
def CIDR2Mask(self):
    """ Функция преобразует CIDR в маску сети
    """
    self = int(self)
    if ((self >= 0) and (self <= 32)):
        mask = bin(2 ** int(self) - 1) + (32 - int(self)) * "0"
        return str(mask)

def dec2bin(self):
    """ Функция преобразует десятичное двухбайтового число в
        двоичный формат с добавлением выравнивающих нулей
    """
    self = int(self)
    if ((self >= 0) and (self <= 255)):
        tmp = bin(int(self))
        return str((8 - len(tmp.lstrip('0b'))) * "0" + tmp.lstrip('0b'))
    
def ip2bin(self):
    """Функция преобразует IP-адрес в двоичный формат
    """
    self = str(self)
    b = re.search("(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})",self)
    if (b):
        return str('0b'+ str(dec2bin(b.group(1))) + str(dec2bin(b.group(2))) + str(dec2bin(b.group(3))) + str(dec2bin(b.group(4))))

def bin2ip(self):
    """ Функция преобразует IP-адрес из двоичного в десятичный формат.
    """
    # Если длина self меньше 34 ( 32 бита + заголовок '0b')
    # то заполняем старшие биты нулями.
    if (len(self) < 34):
        self = '0b' + (32 - len(self.lstrip('0b'))) * '0' + self.lstrip('0b')
    return str(int(self[2:10],2)) + "." + str(int(self[10:18],2)) + "." + str(int(self[18:26],2)) + "." + str(int(self[26:34],2))

# Объявление переменных
File = []   # массив для файла

# Начальный и конечный адреса диапазона
FirstIP = "0.0.0.0"
LastIP =  "255.255.255.255"


# парсинг файла с диапазонами
# в массиве, для удобства обработки, сохраняем данные в бинарном формате.
f = open("IPRangesRaw.txt")
try:
    for line in f:
        # Поиск строк формата: xxx.xxx.xxx.xxx/yy
        c = re.search("(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\/(\d{1,2})",line)
        if (c):
            File.append([ip2bin(c.group(1)),CIDR2Mask(c.group(2))])
            
        # Поиск строк формата: xxx.xxx.xxx.xxx yyy.yyy.yyy.yyy
        m = re.search("(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\s+(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})",line)
        if (m):
            File.append([ip2bin(m.group(1)),ip2bin(m.group(2))])
finally:
    f.close()

# Сортируем массив с адресами по адресам    
File = sorted(File)

# Меняем в массиве File маску на последний адрес диапазона
for i in range(len(File)):
    File[i][1] = bin(operator.xor( (int(CIDR2Mask(32),2) - int(File[i][1],2)) , int(File[i][0],2)))

done = 0 # Метка прохождения всех проверок. Если установлена, то завершаем оптимизацию.
while not(done):
    
    done = 1 # Предположим, что у нас всё оптимизировано и начнём проверку
    
    # Сортирум диапазоны по начальным адресам
    File = sorted(File)
    
    # Начинаем проверять диапазоны, сравнивая File[i] с File[i+1],
    # если условие выполняется, то выполняем оптимизацию, сбрасываем метку выполнения
    # и начинаем просмотр сначала.
    for i in range(len(File) - 1):
        
        # Если оба диапазона начинаются с одного адреса
        if (int(File[i][0],2) == int(File[i+1][0],2)):
            # ...и первый диапазон включает в себя второй, то удаляем второй диапазон
            if (int(File[i][1],2) >= int(File[i+1][1],2)):
                del File[i+1]
                done = 0 
                break
            
            # ...и второй диапазон включает в себя первый, то удаляем первый диапазон
            elif (int(File[i][1],2) < int(File[i+1][1],2)):
                done = 0
                del File[i]
                break
            
        # Если первый дипазон включает в себя второй, то второй удаляем
        elif (int(File[i][0],2) < int(File[i+1][0],2)) and (int(File[i][1],2) >= int(File[i+1][1],2)):
            done = 0
            del File[i+1]
            break
        
        # Если диапазоны следуют друг за другом, то объеденяем их
        elif ((int(File[i][1],2) + 1) == int(File[i+1][0],2)):
            done = 0
            File[i][1] = File[i+1][1]
            del File[i+1]
            break

# Генерируем диапазон исключаемых адресов и пишем в ipfilter.dat        
f = open("ipfilter.dat","w")
try:
    for i in range(len(File)):
        f.write(FirstIP + "-" + bin2ip(bin(int(File[i][0],2) - 1)) + "\n")
        FirstIP = bin2ip(bin(int(File[i][1],2) + 1))
    f.write(FirstIP + "-" + LastIP)
finally:
    f.close()
